#include "ComponentLocomotionBase.h"
#include "Managers/GameManager.h"
#include "Components/ComponentLocomotion.h"
#include "Components/ComponentCreature.h"
#include "Components/ComponentHealth.h"
#include "Components/ComponentDamage.h"
#include "Components/ComponentBody.h"
#include "Components/ComponentLevel.h"

#include "Managers/BlocksManager.h"
#include "Managers/EntityManager.h"
#include "Blocks/LadderBlock.h"
#include "Subsystems/SubsystemTerrain.h"
#include "Subsystems/SubsystemBodies.h"
#include "Subsystems/SubsystemTimeOfDay.h"
#include "Log.h"
#include "Terrain/ScSimplexNoise.h"
#include "GameRandom.hpp"
#include "GameRegistry.hpp"

#include "Subsystems/BlockBehaviors/SubsystemBlockBehaviors.h"
#include "Subsystems/BlockBehaviors/SubsystemWaterBlockBehavior.h"

#include "Network/PackageManager.h"
#include "Network/Packages/SubsystemBodyPackage.h"
#include "GameSingleton.h"
#include "GameThreadPool.hpp"
#include "Expand/EventManager.h"
#include <cfloat>


namespace PocketSurvival
{
    void ComponentLocomotionBase::update(float dt)
    {
        SubsystemTerrain *subsystemTerrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();
        ComponentBody *componentBody = componentCreature->componentBody;

        if (componentBody->componentPlayer != nullptr)
        {
            return;
        }
        
        stunTime = MathUtils::Max<float>(stunTime - dt, 0.0f);
        if (componentCreature->componentHealth->health > 0.0f && stunTime <= 0.0f)
        {
            Vector3 position;;
            componentBody->getPosition(position);

            lastPosition = std::make_shared<Vector3>(position);
            swimBurstRemaining = MathUtils::Saturate((0.1f * swimBurstRemaining) + dt);
            int32_t x2 = Terrain::ToCell(position.posX);
            int32_t y = Terrain::ToCell(position.posY + 0.2f);
            int32_t z = Terrain::ToCell(position.posZ);
            int32_t cellValue = subsystemTerrain->getCellValue(x2, y, z);
            int32_t num = Terrain::ExtractContents(cellValue);
            Block *block = GameSingleton::blocksManager->blocks[num];
            if (ladderSpeed > 0.0f && 
                ladderValue == 0 && 
                block->isBlock<LadderBlock*>() && 
                timeOfDay->getGameTime() >= ladderActivationTime)
            {
                int32_t face = LadderBlock::GetFace(Terrain::ExtractData(cellValue));
                if ((face == 0 && componentBody->collisionVelocityChange.posZ > 0.0f) || 
                    (face == 1 && componentBody->collisionVelocityChange.posX > 0.0f) ||
                    (face == 2 && componentBody->collisionVelocityChange.posZ < 0.0f) ||
                    (face == 3 && componentBody->collisionVelocityChange.posX < 0.0f) ||
                    componentBody->standingOnValue == 0)
                {
                    ladderValue = cellValue;
                    ladderActivationTime = timeOfDay->getGameTime() + 0.2;
                    // 播放动物声音，为完成...
                    // m_componentCreature.ComponentCreatureSounds.PlayFootstepSound(1f);
                }
            }
            Quaternion rotation;
            componentBody->getRotation(rotation);
            
            float num2 = MathUtils::Atan2((2.0f * rotation.Y * rotation.W) - (2.0f * rotation.X * rotation.Z), 1.0f - (2.0f * rotation.Y * rotation.Y) - (2.0f * rotation.Z * rotation.Z));
            num2 += (0.0f - turnSpeed) * turnOrder.posX * dt;
            Quaternion quaternion;
            Quaternion::CreateFromAxisAngle(FastVector3::UnitY, num2, quaternion);
            componentBody->setRotation(quaternion);

            Vector2 lockAngles = lookAngles + (lookSpeed * lookOrder * dt);
            setLookAngles(lockAngles);

            if (ladderValue != 0)
            {
                ladderMovement(dt, cellValue);
            }
            else
            {
                normalMovement(dt);
            }
        }
        else
        {
            componentBody->isGravityEnabled = true;
            componentBody->isGroundDragEnabled = true;
            componentBody->isWaterDragEnabled = true;
        }
        lastWalkOrder = walkOrder;
        lastFlyOrder = flyOrder;
        lastSwimOrder = swimOrder;
        lastTurnOrder = turnOrder;
        lastJumpOrder = jumpOrder;

        setWalkOrder(nullptr);
        setFlyOrder(nullptr);
        setSwimOrder(nullptr);
        setTurnOrder(FastVector2::Zero);
        setJumpOrder(0.0f);
        setLookOrder(Vector2(
            lookAutoLevelX ? (-10.0f * lookAngles.posX / lookSpeed) : 0.0f,
            lookAutoLevelY ? (-10.0f * lookAngles.posY / lookSpeed) : 0.0f
        ));
    }

    void ComponentLocomotionBase::normalMovement(float dt)
    {
        componentCreature->componentBody->isGravityEnabled = true;
        componentCreature->componentBody->isGroundDragEnabled = true;
        componentCreature->componentBody->isWaterDragEnabled = true;

        Vector3 velocity;
        componentCreature->componentBody->getVelocity(velocity);


        // Log::Info(fmt::format("start velocity : {}", velocity.toString()));
        Vector3 right = componentCreature->componentBody->getMatrix().getRight();
        Quaternion quaternion;
        Quaternion::CreateFromAxisAngle(right, lookAngles.posY, quaternion);
        Vector3 vector = Quaternion::Vector3Transform(componentCreature->componentBody->getMatrix().getForward(), quaternion);
        if (walkSpeed > 0.0f && walkOrder != nullptr)
        {
            Vector2 value = *(walkOrder);
            if (walkSpeedWhenTurning > 0.0f && MathUtils::Abs<float>(turnOrder.posX) > 0.02f)
            {
                value.posY = MathUtils::Max<float>(value.posY, MathUtils::Lerp(0.0f, walkSpeedWhenTurning, MathUtils::Saturate(2.0f * MathUtils::Abs<float>(turnOrder.posX))));
            }
            float num2 = walkSpeed;
            if (componentCreature->componentBody->immersionFactor > 0.2f)
            {
                num2 *= 0.66f;
            }
            if (value.posY < 0.0f)
            {
                num2 *= 0.6f;
            }
            if (componentLevel != nullptr)
            {
                num2 *= componentLevel->speedFactor;
            }
            // ComponentMount 未完成...


            Vector3 v4 = (value.posX * Vector3::Normalize(Vector3(right.posX, 0.0f, right.posZ))) + (value.posY * Vector3::Normalize(Vector3(vector.posX, 0.0f, vector.posZ)));
            Vector3 vector2 = (num2 * v4) + componentCreature->componentBody->standingOnVelocity;
            float num4;
            if (componentCreature->componentBody->standingOnValue != 0)
            {
                float num3 = MathUtils::Max<float>(GameSingleton::blocksManager->blocks[Terrain::ExtractContents(componentCreature->componentBody->standingOnValue)]->FrictionFactor, minFrictionFactor);
                num4 = MathUtils::Saturate(dt * 6.0f * accelerationFactor * num3);
            }
            else
            {
                num4 = MathUtils::Saturate(dt * 6.0f * accelerationFactor * inAirWalkFactor);
            }
            velocity.posX += num4 * (vector2.posX - velocity.posX);
            velocity.posZ += num4 * (vector2.posZ - velocity.posZ);
            Vector3 vector3 = (value.posX * right) + (value.posY * vector);
            if (componentLevel != nullptr)
            {
                vector3 *= componentLevel->speedFactor;
            }
            velocity.posY += 10.0f * accelerationFactor * vector3.posY * componentCreature->componentBody->immersionFactor * dt;
            componentCreature->componentBody->isGroundDragEnabled = false;
        }

        if (flySpeed > 0.0f && flyOrder != nullptr)
        {
            Vector3 value3 = *(flyOrder);
            Vector3 v5 = flySpeed * value3;

            
            Vector3 velocityValue = MathUtils::Saturate(2.0f * accelerationFactor * dt) * (v5 - velocity);
            velocity += velocityValue;

            // Log::Info(fmt::format("value3: {}, v5: {},  velocityValue: {}, velocity: {}", value3.toString(), v5.toString(), velocityValue.toString(), velocity.toString()));

            componentCreature->componentBody->isGravityEnabled = false;
        }
        if (swimSpeed > 0.0f && swimOrder != nullptr && componentCreature->componentBody->immersionFactor > 0.5f)
        {
            Vector3 value4 = *(swimOrder);
            Vector3 v6 = swimSpeed * value4;
            float num5 = 2.0f;
            if (value4.lengthSquared() >= 0.99f)
            {
                v6 *= MathUtils::Lerp(1.0f, 2.0f, swimBurstRemaining);
                num5 *= MathUtils::Lerp(1.0f, 4.0f, swimBurstRemaining);
                swimBurstRemaining -= dt;
            }
            velocity += MathUtils::Saturate(num5 * accelerationFactor * dt) * (v6 - velocity);
            componentCreature->componentBody->isGravityEnabled = MathUtils::Abs(value4.posY) <= 0.07f;
            componentCreature->componentBody->isWaterDragEnabled = false;
            componentCreature->componentBody->isGroundDragEnabled = false;
        }
        if (jumpOrder > 0.0f && (componentCreature->componentBody->standingOnValue != 0 || componentCreature->componentBody->immersionFactor > 0.5f) && false == componentCreature->componentBody->isSneaking)
        {
            float num6 = jumpSpeed;
            if (componentLevel != nullptr)
            {
                num6 *= (0.25f * (componentLevel->speedFactor - 1.0f)) + 1.0f;
            }
            velocity.posY = MathUtils::Min<float>(velocity.posY + (MathUtils::Saturate(jumpOrder) * num6), num6);
        }
        componentCreature->componentBody->setVelocity(velocity);
    }

    void ComponentLocomotionBase::ladderMovement(float dt, int32_t value)
    {
        componentCreature->componentBody->isGravityEnabled = false;
        
        Vector3 position;
        Vector3 velocity;
        componentCreature->componentBody->getPosition(position);
        componentCreature->componentBody->getVelocity(velocity);


        int32_t num = Terrain::ExtractContents(value);
        if (GameSingleton::blocksManager->blocks[num]->isBlock<LadderBlock*>())
        {
            ladderValue = value;
            if (walkOrder != nullptr)
            {
                Vector2 value2 = *(walkOrder);
                float num2 = ladderSpeed * value2.posY;
                velocity.posX = 5.0f * (MathUtils::Floor(position.posX) + 0.5f - position.posX);
                velocity.posZ = 5.0f * (MathUtils::Floor(position.posZ) + 0.5f - position.posZ);
                velocity.posY += MathUtils::Saturate(20.0f * dt) * (num2 - velocity.posY);
            }
            if (componentCreature->componentBody->standingOnValue != 0 && timeOfDay->getGameTime() >= ladderActivationTime)
            {
                ladderActivationTime = timeOfDay->getGameTime() + 0.2;
            }
        }
        else
        {
            ladderValue = 0;
            ladderActivationTime = timeOfDay->getGameTime() + 0.2;
        }
        if (jumpOrder > 0.0f)
        {
            velocity += jumpSpeed * componentCreature->componentBody->getMatrix().getForward();
            ladderActivationTime = timeOfDay->getGameTime() + 0.33;
            ladderValue = 0;
        }
        componentCreature->componentBody->setVelocity(velocity);
    }

    void ComponentLocomotionBase::load(const nlohmann::json &jsonData)
    {
        ComponentLocomotion::load(jsonData);
    }

    static const std::string componentName = "Locomotion";
    const std::string &ComponentLocomotionBase::getName() const
    {
        return componentName;
    }
    const std::string &ComponentLocomotionBase::GetName()
    {
        return componentName;
    }
} // namespace PocketSurvival
